/*
This software is copyright 2009 Matthew Wilson matthew@wilson.org
and available under the Artistic License 2 ("AL2") as documented at
http://jsmeta.googlecode.com
*/

ignoreDisqualifyingREs = true;
var deepestComputingCellActivation = false;

function cellActivationGenerator(cellId, cell) {
  return new Func.Act(cellId, function() {
    var comp = this.computation; // activation (expression tree) representing the cell's formula
    switch (this.phase) {
      
      case 0: // phase 0 means (re-)parse the cell's formula (initial phase)
      this.phase = 1;
      this.dep = -1;
      this.deterministic = true;
      Print('gen '+gen+' parsing and compiling formula in '+cellId);
      var parser = formulaParser.reactivate();
      parseContext.__Init(cell.formula);
      parseContext.parsingCell = cell.id;
      cell.dependencies = {};
      comp = this.computation = parser.run(parseContext);
      
      case 1: // Some cell thinks I'm dependent on them; if so, recompute.
      this.calc = randInt(10000);
      Print('gen '+gen+' calc '+this.calc+' using compiled program to compute '+cellId);
      deepestComputingCellActivation = cellId;
      this.result = comp();
      return this.invoker;
    }
  }, []);
}

Module(function JSMetaSheet(l) {
  l.__Language(function Formula(l) { with (l) {
    
    Interleave( Language.Base.Whitespace );
    
    Func.Rule(l, function Atom() { return opil(either(
      useFirst(both(
        useSecond(both(any('('), opil(l.Expression())))
      , opil(any(')'))))
    , Expression()
    , CellReference()
    , RealLiteral()));
    });
    
    Func.Rule(l, function RealLiteral() { return project(
      regexp('\\-?(?:0|[1-9]\\d*)(?:\\.\\d+)?')
    , function() {
        var numericLiteral = this.result*1;
        this.result = function() {
          return numericLiteral;
        };
      }
    )});
    
    function OperatorPrecedence() {
      
      
      return alt.apply({}, alternatives);
    }
    
    Func.Rule(l, function Expression() {
      return OperatorPrecedence(
        either(Addition(), Subtraction())
      , either(Multiplication(), Division())
      , Exponentiation());
    });
    
    Func.Rule(l, function Infix() {
      var operation = this.args[1];
      return project(
        syntax(
          bind("leftOperand", Atom())
        , any(this.args[0])
        , bind("rightOperand", Atom()))
      , function() {
          var left = this.vars.leftOperand,
             right = this.vars.rightOperand;
          this.result = function() {
            return operation.call({}, left, right);
          };
      });
    });
    
    function ExponentiationOp(left, right) {
      return Math.Pow(left(), right());
    }
    
    Func.Rule(l, function Exponentiation() {
      return Infix("**", ExponentiationOp);
    });
    
    function DivisionOp(left, right) {
      return left() / right();
    }
    
    Func.Rule(l, function Division() {
      return Infix("/", DivisionOp);
    });
    
    function MultiplicationOp(left, right) {
      return left() * right();
    }
    
    Func.Rule(l, function Multiplication() {
      return Infix("*", MultiplicationOp);
    });
    
    function SubtractionOp(left, right) {
      return left() - right();
    }
    
    Func.Rule(l, function Subtraction() {
      return Infix("-", SubtractionOp);
    });
    
    function AdditionOp(left, right) {
      return left() + right();
    }
    
    Func.Rule(l, function Addition() {
      return Infix("+", AdditionOp);
    });
    
    Func.Rule(l, function CellReference() {
      return project(regexp('[A-Za-z]+(?:0|[1-9]\\d*)'), function() {
        var cellRef = this.result;
        var myDependents, myDependencies;
        if (l.parsingCell==cellRef) { // || cells[l.parsingCell].deps.has(cellRef) || cells[cellRef].dependencies.has(l.parsingCell)) {
          this.result = function() { return die("Error: invalid formula; circular reference detected: "+[l.parsingCell, cellRef]) };
        } else {
          if (!((myDependencies = cells[l.parsingCell].dependencies)[cellRef])) {
            myDependencies[cellRef] = true;
            Print('added '+cellRef+' to '+l.parsingCell+'\'s list of dependencies');
          } else {
            Print(l.parsingCell+' already had '+cellRef+' in its list of dependencies');
          }
          if (!((myDependents = cells[cellRef].deps)[l.parsingCell])) {
            myDependents[l.parsingCell] = true;
            Print('added '+l.parsingCell+' to '+cellRef+'\'s list of dependents');
          } else {
            Print(cellRef+' already had '+l.parsingCell+' in its list of dependents');
          }
        }
        this.result = function() {
          var act = cells[cellRef].activation;
          act.str = cellRef + '().' + (act.index = computeContext.idx = gen);
          act.invoker = false;
          var result = act.run(computeContext);
          return result*1!==NaN
            ? result*1
            : result; // string result I guess?
        };
      });
    });
    
    Func.Rule(l, function Main() { return alt(
      opil(RealLiteral())
    , useSecond(both(any('='), Expression()))
    , useFirst(both(any('='), opil(end())))
    , useSecond(both(regexpE('^(?=[^=])'), token(plus(any()))))
    , both(empty(), opil(end()))
    )});
/*        project(opil(RealLiteral())
        , function() {
            var numericLiteralFunction = this.result;
            this.result = new Func.Act('numericLiteral', function() {
              this.phase++;
              this.result = this.args[0];
              return this.invoker;
            }, [numericLiteralFunction()]);
          }
        )
      , project(both(any('='), Expression())
        , function() {
            var expressionTree = this.second.result;
            var computation = expressionTree();
            Print('formula parser returning '+computation);
            this.result = computation;
            //Print('computation result: '+this.result);
          }
        )
      , project(both(any('='), end())
        , function() {
            this.result = new Func.Act('nonNumericLiteral', function() {
              this.phase++;
              this.result = '=';
              return this.invoker;
            }, ['=']);
          }
        )
      , project(useSecond(both(regexpE('^(?=[^=])'), token(plus(any()))))
        , function() {
            var nonNumericLiteral = this.result;
            this.result = new Func.Act('nonNumericLiteral', function() {
              this.phase++;
              this.result = this.args[0];
              return this.invoker;
            }, [nonNumericLiteral]);
          }
        )
      , project(both(empty(), end())
        , function() {
            this.result = new Func.Act('emptyCell', function() {
              this.phase++;
              this.result = '';
              return this.invoker;
            }, ['']);
          }
        )
      );
    });*/
  }}).Export();
});

var gen = 0; // generation (revision or stm index, if you will)


var parseContext, formulaParser, computeContext;

